import Matrix from "./matrix";
import Role from "./role";
import Queue from "./queue";

export default class Controller {
  zombies = new Map();
  creatures = new Map();
  #movement = [];
  #gameMap = null;
  #restMoveCount = 0;

  constructor(mapSize = 1) {
    this.#gameMap = new Matrix(mapSize);
  }

  getNewId() {
    return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
      /[xy]/g,
      (c) => {
        let r = (Math.random() * 16) | 0,
          v = c == "x" ? r : (r & 0x3) | 0x8
        return v.toString(16)
      }
    )
  }

  addRole(x, y) {
    if (this.#gameMap.get(x, y).length !== 0) {
      return;
    }
    const id = this.getNewId();
    if (this.zombies.size === 0) {
      const [lx, ly] = this.#gameMap.set(x, y, {
        type: 1,
        id,
      });
      this.zombies.set(id, new Role(id, 1, lx, ly));
    } else {
      const [lx, ly] = this.#gameMap.set(x, y, {
        type: 0,
        id,
      });
      this.creatures.set(id, new Role(id, 0, lx, ly));
    }
  }

  setInitialMovement(movement) {
    if (movement instanceof Array) {
      this.#movement = new Queue(movement)
      for(let item of this.zombies.values()) {
        // 为新僵尸拷贝一份自己的动作队列
        item.restMovement = this.#movement.generate()
        this.#restMoveCount += movement.length
      }
      return
    }
    throw new Error("动作需要为数组")
  }

  get gameMap() {
    return this.#gameMap.value;
  }

  get data() {
    return this.#gameMap.transposed()
  }

  nextTick() {
    const currentSize = this.zombies.size
    let current = 0
    for(let item of this.zombies.values()) {
      if (current >= currentSize) {
        break
      }
      current++
      // 获取下一步
      let movement = item.restMovement.dequeue()
      // 判断该僵尸是否进行完所有的动作
      if (movement === undefined) {
        continue
      }
      movement = movement.toUpperCase()
      // 删除原来位置
      this.#gameMap.set(item.x, item.y, (val) => val.filter((val) => val.id !== item.id))
      // 计算变换前的新位置坐标（允许越界、允许小于0）
      let newLocation = [item.x, item.y]
      if (movement === "R") {
        newLocation[0] += 1
      } else if (movement === "L") {
        newLocation[0] -= 1
      } else if (movement === "U") {
        newLocation[1] -= 1
      } else if (movement === "D") {
        newLocation[1] += 1
      }
      // 在地图上设置新项，并获取变换后的坐标（不允许越界，不允许小于0）
      const [nX, nY] = this.#gameMap.set(...newLocation, {
        type: item.type,
        id: item.id
      })
      // 为map项设置新坐标
      this.zombies.set(item.id, new Role(item.id, 1, nX, nY, item.restMovement))
      // 进行感染操作
      const creatureAmount = this.#gameMap.get(nX, nY).length
      if (creatureAmount > 1) {
        // 当前位置存在其他生物，开始感染
        this.#gameMap.set(nX, nY, (val) => val.map((character) => {
          if (character.type === 0) {
            this.zombies.set(character.id, new Role(character.id, 1, nX, nY, this.#movement.generate()))
            this.creatures.delete(character.id)
            this.#restMoveCount += this.#movement.size
          }
          return {...character, type: 1}
        }))
      }
      this.#restMoveCount -= 1
    }
  }

  execute() {
    while(this.#restMoveCount > 0) {
      this.nextTick()
    }
  }
}
